# 20. 生成器,生成器表达式,推导式
# 生成器
# 生成器函数
生成器函数:yield 返回值,这里要先说明一下,return跟yield的区别
return:直接返回结束,结束函数执行
yield:返回结果,每一个yield就是一段返回值,可以让函数分段执行
如果函数中包含了yield,那么这个函数就不是普通的函数了,而是生成器函数
注意:生成器函数中是无法使用return
def so():
print("情这字伤人又伤心")
yield "心最难治"
wo = so()
print(wo)
执行结果:
<generator object so at 0x0000027B5CAB3308>
由于函数中存在了yield,那么这个函数就是一个生成器函数,要执行这个函数的时候,就不能使用函数的执行了,而是去获取这个生成器函数,需要使用迭代器来获取,因为生成器的本质就是迭代器
def so():
print("情这字伤人又伤心")
yield "心最难治"
wo = so()
print(wo.__next__())
print(wo.__next__())
执行结果:
情这字伤人又伤心
心最难治
StopIteration
这样就实现了生成器函数的获取调用,这样又出现了一个很熟悉的问题,报错:StopIteration
因为上面实例连续获取二次,而生成器函数中的是以一个yield返回值函数为一段返回,第一次获取内容到yield返回值结束为第一次获取的结果,第二次去获取就获取不到数据,就会报错:StopIteration
# 实例(模拟生产)
一个厂家要生产一万件定制高端衣服,需要全记录在案
def so():
wo = []
for i in range(1,10001):
wo.append("高端定制衣服"+ "_" + str(i))
return wo
eo = so()
print(eo)
以上实例,结果虽然出得来,但是是一次性全出来这样会比较占用内存
def so():
for i in range(1,10001):
yield "高端定制衣服"+"_"+str(i)
wo = so()
print(wo.__next__())
print(wo.__next__())
以上实例,使用生成器函数,一次就会拿取一个,可以用多少生成多少,生成器是一个一个的指向下一个,不会回去,next()到哪,指针就到哪,下一次继续获取指针指向的值
# 另一种方法获取生成器中的内容 - send()
先说明一下send跟__next__的区别
next():可以让生成器向下执行一次
send:可以让生成器向下执行一次,并给上一个yield的位置传递一个值,在第一次执行生成器代码的时候不能使用send(),也不能在最后一个yield发送值
def so():
print("早上想吃什么")
a = yield "豆浆油条"
print(a)
print("中午想吃什么")
b = yield "烤肉饭"
print(b)
print("晚上想吃什么")
yield "火锅"
wo = so()
print(wo.__next__())
print(wo.send("———————————"))
print(wo.send("———————————"))
执行结果:
早上想吃什么
豆浆油条
———————————
中午想吃什么
烤肉饭
———————————
晚上想吃什么
火锅
在以上实例中,send的特性就是不能在开头中使用,因为send要给上一个yield传递值,如果在开头中,还有上一个吗
# 生成器也可以用for来获取内容
def so():
print("早上想吃什么")
yield "豆浆油条"
print("中午想吃什么")
yield "烤肉饭"
print("晚上想吃什么")
yield "火锅"
wo = so()
for i in wo:
print(i)
执行结果:
早上想吃什么
豆浆油条
中午想吃什么
烤肉饭
晚上想吃什么
火锅
# 使用列表来获取yield返回值
def so():
yield "早上想吃什么"
yield "豆浆油条"
yield "中午想吃什么"
yield "烤肉饭"
yield "晚上想吃什么"
yield "火锅"
wo = so()
woo = list(wo)
print(woo)
执行结果:
['早上想吃什么', '豆浆油条', '中午想吃什么', '烤肉饭', '晚上想吃什么', '火锅']
# 列表推导式
列表推导式是将循环判断语句合成一行代码
格式:变量 = [ 最终结果 for 变量 in 可迭代对象]
##获取0到15的值-正式循环
so = []
for i in range(16):
so.append(i)
print(so)
##获取0到15的值-列表推导式
so = [ i for i in range(16)]
print(so)
执行结果:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
# 实例 - 无答案
使用列表推导式获取1-100内所有的偶数
使用列表推导式获取1-100内能被3整除的数
使用列表推导式100以内能被3整除的平方
寻找名字中带有两个 e 的人的名字
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
# 实例 - 答案
# 第一题
so = [ i for i in range(1,101) if i%2 == 0]
print(so)
执行结果:
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100]
# 第二题
so = [ i for i in range(1,101) if i%3 == 0]
print(so)
执行结果:
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]
# 第三题
so = [ i*i for i in range(1,101) if i%3 == 0]
print(so)
执行结果:
[9, 36, 81, 144, 225, 324, 441, 576, 729, 900, 1089, 1296, 1521, 1764, 2025, 2304, 2601, 2916, 3249, 3600, 3969, 4356, 4761, 5184, 5625, 6084, 6561, 7056, 7569, 8100, 8649, 9216, 9801]
# 第四题
so = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
wo = [ i for a in so for i in a if i.count("e") == 2]
print(wo)
执行结果:
['Jefferson', 'Wesley', 'Steven', 'Jennifer']
# 生成器表达式
生成器表达式和列表推导式的语法基本是一样的,只是把 [ ] 替换成 ( )
so = (i for i in range(1,16))
for i in so:
print(i)
或
so = (i for i in range(1,16))
print(wo.__next__())
print(wo.__next__())
print(wo.__next__())
print(wo.__next__())
print(wo.__next__())
print(wo.__next__())
。。。。。
以上二种方法执行都是可以的
# 生成器表达式的一个坑规则
def so():
print(1024)
yield 4201
wo = so()
wo1 = (i for i in wo)
wo2 = (i for i in wo1)
print(list(wo))
print(list(wo1))
print(list(wo2))
执行结果:
1024
[4201]
[]
[]
为什么会这样
def so():
print(1024)
yield 4201
wo = so()
wo1 = (i for i in wo)
wo2 = (i for i in wo1)
print(list(wo))
print(list(wo1))
print(list(wo2))
创建函数 so:
打印数字 1024
返回值 4201
引用生成器
使用生成器表达式循环数据源 wo
使用生成器表达式循环数据源 wo1
打印获取wo中的数据,这时so()生成器才会执行
打印获取wo1中的数据,wo1的数据来源是wo,但是wo已经取完了,那wo1也就没有数据了
打印获取wo2中的数据,wo2的数据来源是wo1,wo1的数据来源是wo,但是wo已经取完了,wo1也就没有数据了,wo2也就没有
# 生成器表达式和列表推导式的区别
- 列表推导式:比较消耗内存,一次加载,全部取出,得到的是一个列表
- 生成器表达式:几乎不占用内存,只有在使用的时候才会分配使用内存,得到的是一个生成器
相当于,要去买鸡蛋,列表推导式是直接给你一蓝子鸡蛋 ,生成器表达式则给你一个老母鸡,需要鸡蛋就给你下蛋
生成器的惰性机制:生成器只有在访问的时候才取值,就是说你找他要他才给你,不找他要,他懒得动一下
# 字典推导式
不用说,就是字典类型的推导式
so = {"a":1,"b":2}
wo = {so[i]: i for i in so}
print(wo)
执行结果:
{1: 'a', 2: 'b'}
以上实例,把字典中的key跟value互换
so = [1,2,3,4,5]
wo = ["技术部","业务部","营销部","财务部","人力行政部"]
so_wo = { so[i]:wo[i] for i in range(len(so))}
print(so_wo)
执行结果:
{1: '技术部', 2: '业务部', 3: '营销部', 4: '财务部', 5: '人力行政部'}
以上实例,将列表1中获取的数据跟列表2中的数据合并成一个字典
# 集合推导式
集合抓推导式可以帮我们直接生成一个集合
集合的特点:无序,不重复,所有集合,所以集合推导式自带去重功能
so = [1,2,3,1,2,5,6,4,5,6]
wo = { i for i in so }
print(wo)
执行结果:
{1, 2, 3, 4, 5, 6}
# 题目
最后来一个很难的练习题目来结尾
def so(a,b):
return a + b
def wo():
for ro in range(4):
yield ro
s = wo()
for i in [5,12]:
s = (so(i,n) for n in s)
print(list(s))
建议先想出答案才去执行得到结果